/**
 * <p>Copyright:Copyright(c) 2019</p>
 * <p>Company:Professional</p>
 * <p>Package:org.cesgroup.course.cor</p>
 * <p>File:ChainInvocationHandler.java</p>
 * <p>类更新历史信息</p>
 * @todo chanlong(陈龙) 创建于 Sep 12, 2019 11:15:39 AM
 */
package org.cesgroup.course.interceptor;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.cesgroup.course.interceptor.annon.Intercepts;
import org.cesgroup.course.interceptor.annon.Signature;

/**
 * <p>描述: </p>
 * <p>Company: Professional</p>
 * @author chanlong(陈龙)
 * @date Sep 12, 2019 11:15:39 AM
 * @version 1.0.2019
 */
public class Chain implements InvocationHandler {

	private final Object target;
	private final Interceptor interceptor;
	private final Map<Class<?>, Set<Method>> signatures;

	private Chain(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatures) {
		this.target = target;			// 代理中的目标对象
		this.signatures = signatures;	// 代理对象的签名
		this.interceptor = interceptor;	// 代理的拦截器
	}
	
	// 重写“InvocationHandler”的“invoke”方法
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		try {
			// 获取目标对象的方法集，即目标对象接口的方法列表
			Set<Method> methods = signatures.get(method.getDeclaringClass());
			// 执行代理对象的“intercept”的方法，参数“Invocation”封装了目标对象、目标方法和目标方法的参数列表
			if (methods != null && methods.contains(method)) {
				return interceptor.intercept(new Invocation(target, method, args));
			}
			// 若代理不存在，则直接调用目标对象的方法
			return method.invoke(target, args);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	// 真正执行代理的方法，此处使用的是JDK代理，所以目标对象必须是基于接口的
	public static Object wrap(Object target, Interceptor interceptor) {
		Map<Class<?>, Set<Method>> signatures = getSignatureMap(interceptor);
		Class<?> type = target.getClass();
		Class<?>[] interfaces = getAllInterfaces(type, signatures);
		if (interfaces.length > 0) {
			return Proxy.newProxyInstance(type.getClassLoader(), interfaces, new Chain(target, interceptor, signatures));
		}
		return target;
	}

	// 获取代理对象的签名（该对象的接口类与其中的抽象方法）
	private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
		Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
		if (interceptsAnnotation == null) {
			throw new RuntimeException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());
		}
		Signature[] sigs = interceptsAnnotation.value();
		Map<Class<?>, Set<Method>> signatures = new HashMap<>();
		for (Signature sig : sigs) {
			Set<Method> methods = signatures.computeIfAbsent(sig.type(), k -> new HashSet<>());
			try {
				Method method = sig.type().getMethod(sig.method(), sig.args());
				methods.add(method);
			} catch (NoSuchMethodException e) {
				throw new RuntimeException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);
			}
		}
		return signatures;
	}

	// 根据代理对象的签名，获取目标对象的接口（因为使用JDK代理，因此所有代理行为都是基于接口的）
	private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatures) {
		Set<Class<?>> interfaces = new HashSet<>();
		while (type != null) {
			for (Class<?> c : type.getInterfaces()) {
				if (signatures.containsKey(c)) {
					interfaces.add(c);
				}
			}
			type = type.getSuperclass();
		}
		return interfaces.toArray(new Class<?>[interfaces.size()]);
	}
}
